home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 May: Tool Chest / Developer CD Series Tool Chest (Apple Computer)(May 1999).iso / Tool Chest / Development Kits / MPW etc / MPW-GM / MPW / Examples / 32BitAExamples / Count.a next >
Encoding:
Text File  |  1998-12-03  |  19.5 KB  |  630 lines  |  [TEXT/MPS ]

  1.                 TITLE            'Count - An MPW tool using -model far'
  2. *-------------------------------------------------------------------------------------------
  3. *
  4. * NAME
  5. *        Count.a -- count lines and characters
  6. *
  7. * SYNOPSIS
  8. *        Count [-l] [-c] [file…]
  9. *
  10. * DESCRIPTION
  11. *        "Count" counts the lines and characters in its input and writes the
  12. *        counts to standard output.  If no files are specified, standard input is
  13. *        read.  If more than one file is specified, separate counts are written
  14. *        for each file, one per line, preceded by the file name.  A total is also
  15. *        written following the list of files.
  16. *
  17. * COPYRIGHT
  18. *        Copyright Apple Computer, Inc. 1985-1993
  19. *        All rights reserved.
  20. *
  21. *                 Demonstration of use of "32-bit everything" addressing.
  22. *                                 for MPW 3.2 Assembler.
  23. *
  24. *     Briefly, "32-bit everything" allows the use of the absolute long addressing mode of
  25. * 680x0 instructions in Macintosh code to refer to both code and data.  Normally,
  26. * because everything in the Macintosh is considered relocatable, this addressing mode
  27. * could not be used; the only "absolute" addresses were low memory globals (and there
  28. * have been warnings about using those!).  However, with a patch to LoadSeg to allow
  29. * for a different format of the CODE 0 segment jump table, and to allow run-time patching
  30. * of these "absolute" addresses, it is now possible to use the absolute long addressing
  31. * mode. (Note that the linker adds the patch installer/de-installer code to the user's
  32. * code, so the effect is localized).  Thus, the MPW compilers and linker allow
  33. * "relocatable" (by runtime patch) "absolute" (by 680x0 addressing mode) instructions.
  34. * You invoke the feature by using the "-model far" option on the command line for
  35. * compilers, assembler and linker.
  36. *
  37. *    In addition to the "-model far" command line option, the user must enable the feature on a
  38. * "per-instruction" basis in Asm.  This program is a modified version of the count program provided
  39. * provided in the :Aexamples:folder to demonstrate how to use the Assembler
  40. * to get the 32-bit everything references.  Unlike the compilers, the user must specify
  41. * which instructions will use this feature by explicitly using a specific absolute addressing
  42. * mode syntax ("(Mylabel).L") . Further, the label used must be "imported". 
  43. * To make life easier, there are "implicit" imports provided by the Assembler:
  44. * Within the same file, preceding data references are implicitly imported, 
  45. * as are all preceding module names. (You cannot, at this time, use a 32-bit reference
  46. * to refer to a module name within that module, however). Note that the use of 32-bit 
  47. * addressing in the Assembler is independent of the CODEREFS and DATAREFS directive
  48. * settings, and that the 32-bit displacements of the 68020+ addressing modes are not
  49. * affected; this is for backward compatibility.
  50. *
  51. *   Within this program, then, you will see absolute references to all kinds of data
  52. * references:  simple Data references, qualified field references and field references 
  53. * under a 'WITH' statement.  Since "data" is implicitly imported, no matter where it is
  54. * found, Main data and data defined in the 'DATA' section of a code module can be
  55. * successfully referenced.
  56. *
  57. *     Additionally, but more simply, there are numerous JSRs and JMPs to other code
  58. * modules, showing both intrasegment and intersegment branching.  To show that it is
  59. * possible to mix-n-match, some references are made which use A5 and PC-relative
  60. * addressing. J. Kettenhofen, Cupertino, 10/15/90.
  61. *-------------------------------------------------------------------------------------------
  62.  
  63.     CASE    OBJ
  64.     INCLUDE    'intenv.a'        ; so we can get our args, open files, etc.
  65.     INCLUDE    'signal.a'        ; so we can handle 'Command-.'
  66.     INCLUDE 'TextUtils.a'    ; for NumToString
  67.     IMPORT    INITCURSORCTL    ; to init the spinning beach ball
  68.     IMPORT    ROTATECURSOR    ; for the spinning beach ball
  69.  
  70. RC_Normal    EQU    0
  71. RC_ParmErrs EQU    1
  72. RC_Abort    EQU    2         ; Return codes
  73.  
  74. ;SIGINT    EQU    2
  75.  
  76. EOLChar    EQU    $0D        ; the Return character marks the end of line
  77.     STRING    Pascal        ; length byte strings
  78.     
  79. BufSize    EQU    1024        ; size of input buffer
  80.  
  81. * global data--these declarations outside of any module are allocated and accessed
  82. * relative to register A5
  83. Globals    RECORD
  84. ArgV    DS.L    1        ; the address of our arguments
  85. ArgC    DS.L    1        ; the number of our arguments
  86. RetCode    DC.B    RC_Normal        ; set to RC_ …
  87. CRStr    DC.W    $010D        ; a 'string' that is a return character
  88. Interrupted    DC.B    0    ; not interrupted yet
  89. progname    DS.L    1    ; the address of our name
  90. NumFiles    DC.W    0    ; the number of files to process
  91. WriteChars    DC.B    0    ; TRUE if the user wants line count
  92. WriteLines    DC.B    0    ; TRUE if the user wants char count
  93. Opts        DC.B    0    ; TRUE if user has selected either line or char
  94. LineCount    DC.L    0
  95. CharCount    DC.L    0
  96. TotalLines    DC.L    0
  97. TotalChars    DC.L    0
  98. Max    DC.B    5            ; length of 'Total' string, or the longest filename
  99. myBuf    DS.B    BufSize    ; for reading from the file
  100. curByte    DC.W    -1        ; the current offset in myBuf
  101. lastByte    DS.W    1    ; last valid byte in myBuf
  102.     ENDR
  103.  
  104.  
  105. *******************************************************************
  106. *  ROUTINE        WriteStrings
  107. *  FUNCTION        calls write for an arbitrary number of strings
  108. *  INPUT        a NIL pointer on stack, followed by arbitrary number of string pointers,
  109. *        and the file descriptor
  110. *  OUTPUT        none
  111. *  NOTES        PROCEDURE    WriteStrings (NIL, Str^ …,FD);
  112. *******************************************************************
  113.         SEG 'Write'
  114. WriteStrings    PROC
  115.     Link    A6,#0        ; set up a stack frame
  116.     Move.L    A2,-(SP)    ; and save one permanent register
  117.     LEA        8(A6),A2    ; point A2 at first (last) parameter
  118.  
  119. * next, create a call block for the write routine on the stack
  120.     Clr.L    -(SP)        ; set the length to zero
  121.     SubQ    #8,SP        ; make room for the buffer and fd
  122.     Move.L    (A2)+,(SP)    ; put the file descriptor in its place
  123.  
  124. * now pull the arguments off the stack and write them out
  125. @1    Move.L    (A2)+,D0    ; get the string pointer
  126.     BEQ.S    @0            ; the list of strings is NIL terminated
  127.     Move.L    D0,A0        ; move the pointer so we can use it
  128.     Move.B    (A0)+,11(SP)    ; to move the length byte into the length arg
  129.     Move.L    A0,4(SP)    ; move the pointer into the buffer arg
  130.     JSR        (write).L    ; write it--CASE is significant
  131.     BRA.S    @1            ; and try again
  132.  
  133. *  done writing.  Clean up the stack and return
  134. @0    Move.L    A2,A1        ; we still need this
  135.     Move.L    -4(A6),A2    ; restore A2
  136.     UNLK    A6            ; throw away the scratch stack stuff
  137.     Move.L    (SP),A0        ; get the return address
  138.     Move.L    A1,SP        ; throw away the parameters
  139.     JMP        (A0)            ; and bail out
  140.     ENDPROC
  141.  
  142.  
  143. *******************************************************************
  144. *  ROUTINE        Stop
  145. *  FUNCTION        terminates execution
  146. *  INPUT        Message(A6)--error message to display on exit
  147. *  OUTPUT        Tool execution is terminated--return to MPW shell
  148. *  NOTES        call with a JMP, not a JSR--it doesn't return to caller anyway
  149. *******************************************************************
  150.     SEG 'STOP'
  151. Stop    PROC
  152.  
  153. *  don't bother to save permanent registers--we're never going back to the caller
  154.     WITH    Globals
  155.     MoveQ    #0,D0
  156.     Move.B    (RetCode).L,D0        ; we'll return this status
  157.     TST.B    Interrupted
  158.     BEQ.S    @1
  159.     Move.B    #RC_Abort,D0    ; unless we were interrupted
  160.  
  161. @1    Move.L    D0,-(SP)
  162.     JSR        (exit).L        ; (does not return)
  163.     ENDWITH
  164.     ENDPROC
  165.  
  166.  
  167. *******************************************************************
  168. *  ROUTINE        Intr
  169. *  FUNCTION        sets the global Interrupted to TRUE--passed to the Runtime routine
  170. *  INPUT
  171. *  OUTPUT        Interrupted is set TRUE
  172. *  NOTES
  173. *******************************************************************
  174.  
  175. Intr    PROC
  176.     ST    Globals.Interrupted
  177.     RTS
  178.     ENDPROC
  179.  
  180.  
  181. *******************************************************************
  182. *  ROUTINE        SyntaxError
  183. *  FUNCTION        Report a syntax error for the command line
  184. *  INPUT        above(A7)--pointers to strings to append to the error message
  185. *  OUTPUT        displays error message and calls Stop to terminate program execution
  186. *  NOTES        call with a JMP, not a JSR--it doesn't return anyway
  187. *******************************************************************
  188.  
  189. SyntaxError    PROC
  190.  
  191.     WITH    Globals
  192.     DC.W    $A9FF
  193.     PEA        #' - '
  194.     Move.L    (progName).L,-(SP)
  195.     PEA        #'### '
  196.     PEA        DiagnosticFD
  197.     JSR        (WriteStrings).L        ; finish writing the error line
  198.     CLR.L    -(SP)
  199.     PEA        CrStr
  200.     PEA        #' [-l] [-c] [files…].'
  201.     Move.L    progName,-(SP)
  202.     PEA        #'# Usage - '
  203.     PEA        (DiagnosticFD).L
  204.     JSR        (WriteStrings).L        ; and write the 'usage' line
  205.     JMP        (Stop).L
  206.     ENDWITH
  207.     ENDPROC
  208.  
  209.  
  210.  
  211. *******************************************************************
  212. *  ROUTINE        LetterOpt
  213. *  FUNCTION        Set a letter    option
  214. *  INPUT        D0--char
  215. *        D4--ArgVIndex
  216. *        A1--address of current option
  217. *  OUTPUT        if char = valid option, set option flag, else syntaxerror
  218. *  NOTES        PROCEDURE    LetterOpt(Opt: Char; VAR ArgVIndex: Integer);
  219. *        ArgVIndex can be updated by this routine to skip arguments to options
  220. *******************************************************************
  221.  
  222. LetterOpt    PROC
  223.     Cmp.B    #'l',D0
  224.     BEQ.S    @0
  225.     Cmp.B    #'L',D0                    ; -l?
  226.     BNE.S    @1
  227. @0    ST        (Globals.WriteLines).L    ; means only lines
  228.     ADDQ.B    #1,Globals.Opts            ; yes, an option has been selected
  229.     RTS
  230. @1    Cmp.B    #'c',D0                    ; -c?
  231.     BEQ.S    @2
  232.     Cmp.B    #'C',D0
  233.     BNE.S    @3
  234. @2    ST        Globals.WriteChars    ; means only characters
  235.     ADDQ.B    #1,Globals.Opts        ; yes, an option has been selected
  236.     RTS
  237. @3    Clr.L    -(SP)            ; otherwise it's a bad option
  238.     PEA        Globals.CRStr
  239.     PEA        #'" is not an option.'
  240.     Move.L    A1,-(SP)        ; pointer to current option
  241.     PEA        #'"'            ; the leading quote around the option
  242.     JMP        SyntaxError
  243. *  SyntaxError never returns
  244.     ENDPROC
  245.  
  246.  
  247. *******************************************************************
  248. *  ROUTINE        Init
  249. *  FUNCTION        Tool initalization
  250. *  INPUT
  251. *  OUTPUT
  252. *  NOTES        PROCEDURE Init;
  253. *******************************************************************
  254.     SEG 'INIT'
  255. Init    PROC
  256.  
  257. ForPascal    EQU    1        ; Make envp & argv strings "Pascal" format
  258.                         ; (preceded with a length byte)
  259.  
  260. ; Comments about the Init.SF structure
  261. ; 1. TEMPLATE. Note that this is a template, not a record;
  262. ; it is equivalent to a 'struct' in C or a type
  263. ; definition in Pascal.  It does not allocate space.
  264. ; 2. DECREMENT. It is used to describe a "stack frame".  Since
  265. ; the stack grows downward, the keyword 'Decrement' is used.
  266. ; 3. The bracketed parameter, '{OldA6}'. This tells the Assembler to
  267. ; calculate field offsets using this field as the '0' offset.
  268. ; This is explained more fully in the MPW Assembler Manual.
  269. ; 4. SIZE field. The size field here is the calculated as the 
  270. ; difference from 0...this turns out to be -4, which
  271. ; turns out to be the amount of space that we have to
  272. ; allocate on the stack via a Link instruction for our
  273. ; local parameters.
  274. ; If we were to have incoming parameters, then we
  275. ; could allocate them above the OldA6 field; what
  276. ; order the parameters are found, and their relation
  277. ; to the routine Return Address varies between C and
  278. ; Pascal; appendices in the MPW language manuals contain
  279. ; more details about language calling conventions.
  280.  
  281. InitSF    RECORD    {OldA6},DECREMENT
  282. ShellRet    DS.L    1        ;'RetPC' in Ch. 12 of MPW Manual
  283. RetAddress    DS.L    1        
  284. OldA6        DS.L    1
  285. EnvP        DS.L    1
  286. Size        EQU    *
  287.     ENDR
  288.  
  289.     WITH    Globals
  290.     Link    A6,#InitSF.Size
  291.     PEA        ForPascal            ; optimized Move.L #1,-(SP)
  292.     PEA        InitSF.EnvP(A6)        ; for Shell Exported variables
  293.     PEA        ArgV                ; Address to store ptr to Command Line Arguments
  294.     PEA        ArgC                ; Address to store ptr to # of Cmd Line Args.
  295.     Move.L    InitSF.ShellRet(A6),-(SP)
  296.     JSR        (_RTInit).L            ; get things set up
  297.     LEA        InitSF.Size(A6),SP    ; throw away the arguments
  298.     PEA        Intr                ; our interrupt handler
  299.     Move.L    #SIGINT,-(SP)
  300.     JSR        (signal).L        ; so we can handle user interrupts
  301. * D0 has handle to prevSig, which we will ignore
  302.     LEA        InitSF.Size(A6),SP    ; throw away the arguments
  303.  
  304.     MoveM.L    A2/D3-D4,-(SP)    ; let's do some ArgV processing
  305.     Move.L    ArgV,A2
  306.     Move.L    ArgC,D3
  307.     Move.L    (A2)+,progName    ; we now have a global that points to our name
  308.     Move.B    #RC_ParmErrs,(RetCode).L
  309.     MoveQ    #0,D4            ; ArgVIndex := 0;
  310. @0    AddQ    #1,D4
  311.     Cmp.L    D4,D3
  312.     BLE.S    DoneArgOptions
  313.     Move.L    (A2)+,A0        ; get the next arg
  314.     Move.L    A0,A1            ; keep a pointer to the start of the string
  315.     Move.B    (A0)+,D1        ; get the len
  316.     BEQ.S    @0                ; arg := ''; get the next one
  317.     Move.B    (A0)+,D0
  318.     Cmp.B    #'-',D0            ; is it an option?
  319.     BNE.S    @1
  320.     Move.B    (A0)+,D0
  321.     JSR        (LetterOpt).L
  322. * caller to LetterOpt can check if ArgIndex changed--if so, skip the increment of ArgIndex
  323.     BRA.S    @0        ; go again
  324. @1    AddQ    #1,NumFiles        ; bump the file count
  325.     Cmp.B    Max,D1            ; a new longest name?
  326.     BLE.S    @0
  327.     Move.B    D1,Max            ; a new max
  328.     BRA.S    @0
  329.  
  330. DoneArgOptions
  331.     Move.B    #RC_Normal,RetCode        ; parameters ok so far
  332.     Clr.L    -(SP)
  333.     JSR        (InitCursorCtl).L        ; initialize the spinning cursor
  334.     Tst.B    Interrupted                ; user break yet?
  335.     BEQ.S    @3
  336.     JMP        Stop
  337. @3    MoveM.L    (SP)+,A2/D3-D4
  338.     UNLK    A6
  339.     RTS
  340.     ENDWITH
  341.     ENDPROC
  342.  
  343. *******************************************************************
  344. *  ROUTINE        PrintCount
  345. *  FUNCTION        writes the filename (if needed), linecount and/or charcount to standard output
  346. *  INPUT        pointer to the filename in A2 (if counting multiple files)
  347. *  OUTPUT
  348. *  NOTES
  349. *******************************************************************
  350.  
  351. PrintCount    PROC
  352. PrintSF        RECORD    0,DECREMENT
  353. LineBuf        DS.B    256
  354. tempStr        DS.B    10
  355. MaxBlanks    DS.B    1
  356.         ALIGN
  357. Size        EQU        *
  358.     ENDR
  359.  
  360.     LINK    A6,#PrintSF.Size
  361.     MoveM.L    D6/A3,-(SP)
  362.  
  363.     Move.W    #(256/4)-1,D0            ; fill LineBuf with blanks
  364.     Move.L    #'    ',D1
  365.     LEA        PrintSF.LineBuf(A6),A0
  366. @0    Move.L    D1,(A0)+
  367.     DBRA    D0,@0
  368.  
  369.     WITH    Globals
  370.     MoveQ    #3,D6            ; skip first three blanks
  371.     LEA        4+PrintSF.LineBuf(A6),A3    ; A3 is the current offset into lineBuf
  372.     Cmp.W    #1,NumFiles        ; >1 if more than one file
  373.     BLE.S    noName
  374.  
  375.     Move.L    D7,A0            ; D7 points to the current filename
  376.     MoveQ    #0,D1
  377.     Move.B    (A0)+,D1        ; get the length byte
  378.     Add.B    D1,D6            ; update the new length
  379.     MoveQ    #0,D0
  380.     Move.B    Max,D0            ; Max is the longest name
  381.     Sub.B    D1,D0            ; D0 is how much shorter current is than max
  382.     AddQ    #3,D0
  383.     Add.B    D0,D6            ; update the counter
  384.     BRA.S    @2                ; zero base the length
  385. @1    Move.B    (A0)+,(A3)+
  386. @2    DBRA    D1,@1            ; move in the filename
  387.     Add.W    D0,A3            ; and update our roving pointer
  388.  
  389. noName
  390.     ENTRY    DoLines,DoChars,WriteBuf
  391.     ; if no options selected, print both lines and chars.
  392.     TST.B    Opts
  393.     BNE.S    @0
  394.      
  395.      JSR    DoLines            ; insert lines into buffer
  396.      JSR    DoChars            ; insert chars into buffer
  397.      BRA.S    @Exit
  398.  
  399. @0    TST.B    WriteLines        ; do we want to print the line count?
  400.     BEQ.S    @1
  401.      JSR    DoLines            ; insert lines into buffer
  402. @1    TST.B    WriteChars
  403.     BEQ.S    @Exit
  404.      JSR    DoChars            ; insert chars into buffer
  405.  
  406. @Exit    
  407.     Move.B    D6,PrintSF.lineBuf(A6)    ; set the length byte
  408.  
  409.     CLR.L    -(SP)            ; set up the stack for WriteStrings
  410.     PEA        CRStr
  411.     PEA        PrintSF.linebuf(A6)
  412.     PEA        OutputFD
  413.     JSR        WriteStrings
  414.     MoveM.L    (SP)+,D6/A3
  415.     UNLK    A6
  416.     RTS
  417.  
  418.         
  419. DoLines         
  420.     Move.L    lineCount,D0
  421.     add.w    #10,D6            ; update counter
  422.     JMP        WriteBuf
  423.     
  424. DoChars
  425.     MOVE.L    charcount,D0
  426.     add.w    #13,D6            ; add field length and 3 blanks to counter
  427.     JMP        WriteBuf
  428.  
  429. WriteBuf    
  430.     LEA    PrintSF.tempStr(A6),A0
  431.     _NumToString
  432.  
  433.     MoveQ    #0,D1
  434.     Move.B    (A0)+,D1
  435.     MoveQ    #10,D0        ; we'll say this field is 10 long
  436.     Sub.B    D1,D0        ; D0 := field length-length of numstring
  437.     Add.W    D0,A3        ; skip the extra padding
  438.  
  439.     BRA.S    @2            ; zero base by doing the DBCC first
  440. @1    Move.B    (A0)+,(A3)+
  441. @2    DBRA    D1,@1        ; move in the number
  442.     RTS
  443.     ENDWITH
  444.     ENDPROC
  445.  
  446.  
  447. *******************************************************************
  448. *  ROUTINE        PrintTotals
  449. *  FUNCTION        writes the summary line to standard output
  450. *  INPUT
  451. *  OUTPUT
  452. *  NOTES        calls PrintCount to print the totals if appropriate
  453. *******************************************************************
  454.     SEG 'PrintTotals'
  455. PrintTotals    PROC
  456.     Cmp.W    #1,Globals.numFiles
  457.     BGT.S    @0
  458.     RTS            ; do nothing if only one file
  459. @0    LEA        #'Total',A0
  460.     Move.L    A0,D7        ; our new 'filename'
  461.     Move.L    Globals.totallines,Globals.linecount
  462.     Move.L    Globals.totalchars,Globals.charcount
  463.     JSR        (PrintCount).L        ; recycled code
  464.     RTS
  465.  
  466.     ENDPROC
  467.  
  468.  
  469. *******************************************************************
  470. *  ROUTINE        GetChar
  471. *  FUNCTION        reads from the file in hunks, and hands out a character at a time
  472. *  INPUT        fd:  long in D4--the file descriptor for the file to read
  473. *  OUTPUT        the next character in D0--zero = TRUE means end of file
  474. *  NOTES
  475. *******************************************************************
  476.     SEG 'GetChar'
  477. GetChar    PROC
  478.     WITH    Globals
  479.     Move.W    curByte,D1    ; get the current offset
  480.     BPL.S    @0            ; we have a valid block currently
  481. @1    PEA        BufSize        ; Move.L #BufSize,-(SP)--count
  482.     PEA        mybuf        ; where
  483.     Move.L    D4,-(SP)    ; the file descriptor
  484.     JSR        (read).L    ; read the next block
  485.     LEA    12(SP),SP        ; clean up the stack
  486.     MoveQ    #0,D1        ; start at the beginning again
  487.     Move.W    D0,lastByte
  488.     BNE.S    @2            ; end of file?
  489.     RTS                    ; pass the zero flag back to the caller
  490. @0    Move.W    lastByte,D0    ; get the last valid byte
  491. @2    Cmp.W    D0,D1
  492.     BGE.S    @1
  493.     LEA        mybuf,A0
  494.     Move.B    0(A0,D1),D0    ; read the next character
  495.     AddQ.W    #1,D1
  496.     Move.W    D1,curByte    ; update curByte
  497.     RTS
  498.     ENDWITH
  499.     ENDPROC
  500.  
  501.  
  502. *******************************************************************
  503. *  ROUTINE        CountFile (fd:filedescriptor)
  504. *  FUNCTION        counts the lines and characters in fd
  505. *  INPUT        fd:  long--the file descriptor for the file to count
  506. *  OUTPUT        charcount, linecount, totalchars, totallines updated
  507. *  NOTES
  508. *******************************************************************
  509.     SEG 'CountFile'
  510. CountFile    PROC
  511.     WITH    globals
  512.     CLR.L    linecount
  513.     Move.L    (SP)+,A1    ; save the return address
  514.     Move.L    (SP),D0        ; and the file descriptor
  515.     Move.L    A1,(SP)        ; return the return address
  516.  
  517.     MoveM.L    D4-D7,-(SP)
  518.     Move.L    D0,D4        ; save the fd for the getchar routine
  519.     MoveQ    #0,D7        ; initialize our counter registers
  520.     MoveQ    #0,D6
  521.  
  522. ReadLoop    
  523.     JSR        getchar
  524.     BEQ.S    fileEnd        ; zero means no more bytes to read
  525.     AddQ.L    #1,D7        ; otherwise bump the char counter
  526.     Move.B    D0,D5        ; save the char in a permanent register
  527.     CMP.B    #EOLChar,D5    ; bump linecount?
  528.     BNE.S    ReadLoop
  529.  
  530.     AddQ.L    #1,D6        ; yes
  531.     Move.L    D6,-(SP)
  532.     JSR        (RotateCursor).L    ; spin the ball
  533.     Tst.B    Interrupted        ; user break yet?
  534.     BEQ.S    ReadLoop        ; no--continue
  535.     JMP        (Stop).L        ; abort mission
  536.  
  537. fileEnd    
  538.     CMP.B    #EOLChar,D5    ; was the last character read a line end?
  539.     BEQ.S    @0
  540.     TST.L    D7        ; have we counted any characters
  541.     BEQ.S    @0        ; no--don't increment line count
  542.     AddQ.L    #1,D6
  543. @0    Move.L    D6,lineCount        ; update globals and leave
  544.     Move.L    D7,charCount
  545.     Add.L    D6,totallines
  546.     Add.L    D7,totalchars
  547.     MoveM.L    (SP)+,D4-D7
  548.     RTS
  549.     ENDPROC
  550.  
  551.  
  552. *******************************************************************
  553. *  ROUTINE        Count
  554. *  FUNCTION        the MAIN proc--calls Init, then processes the files
  555. *  INPUT
  556. *  OUTPUT
  557. *  NOTES
  558. *******************************************************************
  559.  
  560.     SEG 'MAIN'
  561. Count    MAIN
  562.     IMPORT    c2pstr,p2cstr
  563.     WITH    Globals
  564.     JSR        (Init).L
  565.     Move.L    ArgV,A2
  566.     ADDQ    #4,A2        ; skip the program name
  567.     Move.L    (A2)+,D7    ; set the cc's
  568.     BNE.S    @0            ; otherwise count stdin
  569.  
  570. * CountStdIn
  571.     Clr.L    -(SP)        ; we don't need to open standard input
  572.     JSR        (CountFile).L
  573.     JSR        (PrintCount).L
  574.     JMP        (Stop).L
  575.  
  576. @1    Move.L    (A2)+,D7    ; set the cc's
  577.     BEQ.S    ShowTotals    ; ArgV is NIL terminated
  578.  
  579. @0    Move.L    D7,A0
  580.     Move.B    (A0)+,D0    ; pick up the length byte
  581.     BEQ.S    @1            ; zero length--next, please
  582.     Move.B    (A0)+,D1    ; now the first charcter
  583.     Cmp.B    #'-',D1        ; an option--already handled by Init
  584.     BEQ.S    @1
  585.  
  586. * otherwise we have a file to process
  587.     Move.L    D7,-(SP)        ; convert the filename to a C string
  588.     JSR        (p2cstr).L
  589.     PEA        O_RDONLY
  590.     Move.L    D7,-(SP)
  591.     JSR        (open).L        ; open the file
  592.     Move.L    D0,D6            ; save the result--fd or error
  593.     JSR        (c2pstr).L        ; love those length bytes
  594.     LEA        12(SP),SP        ; throw away the arguments
  595.     Move.L    D6,-(SP)        ; push the fd
  596.     BMI.S    BailOut            ;  an error if negative
  597.     JSR        (CountFile).L
  598.     JSR        (PrintCount).L
  599.     BRA.S    @1
  600.  
  601. ShowTotals
  602.     JSR        (PrintTotals).L
  603.     JMP        (Stop).L
  604.  
  605. BailOut    CLR.L    (SP)        ; space came from move D6 above
  606.     PEA        CRStr
  607.     Move.L    D7,-(SP)
  608.     PEA        #' - could not open file '
  609.     Move.L    progName,-(SP)
  610.     PEA        #'### '
  611.     PEA        DiagnosticFD    ; optimized Move.L #DiagnosticFD,-(SP)
  612.     JSR        (WriteStrings).L
  613.  
  614.     CLR.L    -(SP)
  615.     PEA        CRStr
  616.     PEA        #' [-l] [-c] [files…].'
  617.     Move.L    progName,-(SP)
  618.     PEA        #'# Usage - '
  619.     PEA        DiagnosticFD    ; optimized Move.L #DiagnosticFD,-(SP)
  620.     JSR        (WriteStrings).L
  621.     Move.B    #RC_ParmErrs,RetCode
  622.     JMP        (Stop).L
  623.  
  624.     ENDWITH
  625.     ENDPROC
  626.  
  627.     END
  628.  
  629.